package org.frame.gateway.token;

import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.frame.common.core.StringUtils;
import org.frame.common.core.constants.Constants;
import org.frame.common.core.entity.TokenUser;
import org.frame.common.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;

/**
 * token验证处理
 * 
 * @author ruoyi
 */
@Slf4j
@Component
public class TokenService {
	// 令牌自定义标识
	@Value("${token.header:Authorization}")
	private String header;

	// 令牌秘钥
	@Value("${token.secret:abcdefghijklmnopqrstuvwxyz}")
	private String secret;

	// 令牌有效期（默认30分钟）
	@Value("${token.expireTime:30}")
	private int expireTime;

	protected static final long MILLIS_SECOND = 1000;

	protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

	private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

	@Autowired
	private RedisCache redisCache;

	/**
	 * 获取用户身份信息
	 * 
	 * @return 用户信息
	 */
	public TokenUser getTokenUser(HttpServletRequest request) {
		// 获取请求携带的令牌
		String token = getToken(request);
		if (StringUtils.isNotEmpty(token)) {
			Claims claims = parseToken(token);
			// 解析对应的权限以及用户信息
			String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
			String userKey = getTokenKey(uuid);
			TokenUser user = redisCache.getCacheObject(userKey);
			return user;
		}
		return null;
	}

	/**
	 * 获取用户身份信息
	 * 
	 * @return 用户信息
	 */
	public TokenUser getTokenUser(ServerHttpRequest request) {
		// 获取请求携带的令牌
		String token = getToken(request);
		if (StringUtils.isNotEmpty(token)) {
			Claims claims = parseToken(token);
			// 解析对应的权限以及用户信息
			String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
			String userKey = getTokenKey(uuid);
			TokenUser user = redisCache.getCacheObject(userKey);
			return user;
		}
		return null;
	}

	/**
	 * 验证令牌有效期，相差不足20分钟，自动刷新缓存
	 * 
	 * @param token 令牌
	 * @return 令牌
	 */
	public void verifyToken(TokenUser TokenUser) {
		long expireTime = TokenUser.getExpireTime();
		long currentTime = System.currentTimeMillis();
		if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
			String token = TokenUser.getToken();
			TokenUser.setToken(token);
			refreshToken(TokenUser);
		}
	}

	/**
	 * 刷新令牌有效期
	 * 
	 * @param TokenUser 登录信息
	 */
	public void refreshToken(TokenUser TokenUser) {
		TokenUser.setLoginTime(System.currentTimeMillis());
		TokenUser.setExpireTime(TokenUser.getLoginTime() + expireTime * MILLIS_MINUTE);
		// 根据uuid将TokenUser缓存
		String userKey = getTokenKey(TokenUser.getToken());
		redisCache.setCacheObject(userKey, TokenUser, expireTime, TimeUnit.MINUTES);
	}

	/**
	 * 从令牌中获取数据声明
	 *
	 * @param token 令牌
	 * @return 数据声明
	 */
	private Claims parseToken(String token) {
		return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
	}

	/**
	 * 从令牌中获取用户名
	 *
	 * @param token 令牌
	 * @return 用户名
	 */
	public String getUsernameFromToken(String token) {
		Claims claims = parseToken(token);
		return claims.getSubject();
	}

	/**
	 * 获取请求token
	 *
	 * @param request
	 * @return token
	 */
	private String getToken(HttpServletRequest request) {
		String token = request.getHeader(header);
		if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
			token = token.replace(Constants.TOKEN_PREFIX, "");
		}
		return token;
	}

	/**
	 * 获取请求token
	 *
	 * @param request
	 * @return token
	 */
	private String getToken(ServerHttpRequest request) {
		HttpHeaders headers = request.getHeaders();
		String token = headers.getFirst(header);
		if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
			token = token.replace(Constants.TOKEN_PREFIX, "");
		} else {
			token = request.getQueryParams().getFirst("token");
		}
		log.debug("本次请求token：{}", token);
		return token;
	}

	private String getTokenKey(String uuid) {
		return Constants.LOGIN_TOKEN_KEY + uuid;
	}
}
